package com.ljl.module.system.service.impl;

import com.ljl.module.system.domain.TransferJob;
import com.ljl.module.system.util.Callback;
import com.ljl.module.system.service.FfmpegService;
import com.ljl.module.system.util.Utils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.annotation.PreDestroy;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author 20664
 */
@Slf4j
@Service
public class FfmpegServiceImpl implements FfmpegService {
    private static final Pattern VERSION_PATTERN = Pattern.compile("version (\\d+\\.\\d+\\.\\d+)-");
    private static final Pattern SIZE_PATTERN = Pattern.compile("size=\\D+(\\d*)kB");
    private static final String MERGE_CMD = "ffmpeg -i %s -i %s -c:v copy -c:a copy %s";
    private static final ConcurrentHashMap<String, Process> PROCESS_MAP = new ConcurrentHashMap<>();

    @Override
    public String getVersion() throws IOException {
        Runtime rt = Runtime.getRuntime();
        Process exec = rt.exec("ffmpeg -version");
        try {
            exec.waitFor(5, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            log.error("getVersion error", e);
        }
        return Utils.getStreamMsg(exec.getInputStream(), (line) -> {
            Matcher matcher = VERSION_PATTERN.matcher(line);
            if (matcher.find()) {
                return matcher.group(1);
            }
            return null;
        });
    }

    @Override
    public boolean mergeAudioAndVideo(TransferJob transferJob, Callback<Float> progress) throws Exception {
        String videoPath = transferJob.getVideoPath();
        String audioPath = transferJob.getAudioPath();
        if (!StringUtils.hasText(videoPath) || !StringUtils.hasText(audioPath)) {
            log.error("文件路径为空, video {}, audio {}", videoPath, audioPath);
            throw new FileNotFoundException(String.format("文件路径为空, video: %s, audio: %s", videoPath, audioPath));
        }
        File video = new File(videoPath);
        File audio = new File(audioPath);
        if (!(video.exists()) || !(audio.exists())) {
            log.error("文件不存在, video {}, audio {}", videoPath, audioPath);
            throw new FileNotFoundException(String.format("文件不存在, video: %s, audio: %s", videoPath, audioPath));
        }
        // 视频文件大小
        long videoLength = video.length();
        // 音频文件大小
        long audioLength = audio.length();
        long totalLength = videoLength + audioLength;
        BigDecimal total = BigDecimal.valueOf(totalLength);

        Process process = PROCESS_MAP.get(transferJob.getUuid());
        if (process != null) {
            log.error("任务已存在： {}", transferJob.getUuid());
            throw new IllegalArgumentException(String.format("任务已存在, uiid: %s", transferJob.getUuid()));
        }
        Runtime rt = Runtime.getRuntime();
        Process exec = rt.exec(String.format(MERGE_CMD, videoPath, audioPath, transferJob.getOutputPath()));
        PROCESS_MAP.put(transferJob.getUuid(),exec);
        AtomicBoolean startTransferFlag = new AtomicBoolean(false);
        try {
            Utils.getStreamMsg(exec.getErrorStream(), (line) -> {
                Matcher matcher = SIZE_PATTERN.matcher(line);
                if (matcher.find()) {
                    return matcher.group(1);
                }
                return null;
            }, s -> {
                startTransferFlag.set(true);
                log.info("已转换大小:{}", s);
                long currentSize = Long.parseLong(s);
                currentSize *= 1000;
                BigDecimal current = BigDecimal.valueOf(currentSize);
                BigDecimal p = current.multiply(BigDecimal.valueOf(100)).divide(total, 2, RoundingMode.HALF_UP);
                if (p.compareTo(BigDecimal.valueOf(100)) > -1) {
                    progress.call(100f);
                } else {
                    progress.call(p.floatValue());
                }
            });
        } finally {
            // 销毁进程
            destroyProcess(exec);
        }
        progress.call(100f);
        return startTransferFlag.get();
    }

    @PreDestroy
    private void destroy() {
        // 清理进程
        PROCESS_MAP.values().forEach(this::destroyProcess);
    }

    /**
     * 清理process
     *
     * @param exec /
     */
    private void destroyProcess(Process exec) {
        if (exec != null) {
            // 尝试正常终止
            exec.destroy();
            try {
                // 等待一段时间看进程是否结束
                if (!exec.waitFor(5, TimeUnit.SECONDS)) {
                    // 如果超时，则强制终止
                    exec.destroyForcibly();
                }
            } catch (Exception e) {
                e.printStackTrace();
                Thread.currentThread().interrupt();
                // 处理中断异常
            }
        }
    }

}
